/*
 * DocsPlugin.java
 * 
 * Copyright (c) 2007
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.dcarew.googledocs;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.dcarew.googledocs.utils.DocsUtils;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;

import com.google.gdata.client.docs.DocsService;
import com.google.gdata.client.spreadsheet.FeedURLFactory;
import com.google.gdata.client.spreadsheet.SpreadsheetService;
import com.google.gdata.data.docs.DocumentListFeed;
import com.google.gdata.data.spreadsheet.SpreadsheetEntry;
import com.google.gdata.data.spreadsheet.SpreadsheetFeed;
import com.google.gdata.util.AuthenticationException;
import com.google.gdata.util.ServiceException;

// TODO: New doc menu item - allows the user to create a new doc


/**
 * The activator class controls the plug-in life cycle.
 * 
 * @author Devon Carew
 */
public class DocsPlugin
	extends AbstractUIPlugin
{
	public static final String 			PLUGIN_ID = "org.dcarew.googledocs";
	
	// [company-id]-[app-name]-[app-version]
	public static final String 			APPLICATION_NAME = "org.dcarew-EclipseDocsClient-0.9.5";
	
	private static DocsPlugin			plugin;
	
	private static DocsService			docsService;
	private static SpreadsheetService 	spreadsheetService;
	
	//private static final String DEFAULT_AUTH_PROTOCOL = "https";
	//private static final String DEFAULT_AUTH_HOST = "docs.google.com";
	
	private static final String 		DEFAULT_PROTOCOL = "http";
	private static final String 		DEFAULT_HOST = "docs.google.com";
	
	//private static final String SPREADSHEETS_SERVICE_NAME = "wise";
	//private static final String SPREADSHEETS_HOST = "spreadsheets.google.com";
	
	private static final String 		URL_FEED = "/feeds";
	//private static final String URL_DOWNLOAD = "/download";
	
	private static final String 		URL_GROUP_DOCUMENTS = "/documents";
	//private static final String URL_GROUP_FOLDERS = "/folders";
	//private static final String URL_GROUP_MEDIA = "/media";
	//private static final String URL_GROUP_ACL = "/acl";
	
	private static final String 		URL_PATH = "/private/full";
	
	private static final String 		URL_CATEGORY_DOCUMENT = "/-/document";
	private static final String 		URL_CATEGORY_SPREADSHEET = "/-/spreadsheet";
	private static final String 		URL_CATEGORY_PDF = "/-/pdf";
	private static final String 		URL_CATEGORY_PRESENTATION = "/-/presentation";
	private static final String 		URL_CATEGORY_STARRED = "/-/starred";
	private static final String 		URL_CATEGORY_TRASHED = "/-/trashed";
	private static final String 		URL_CATEGORY_FOLDER = "/-/folder";
	//private static final String URL_CATEGORY_EXPORT = "/Export";
	
	private static final String 		PARAMETER_SHOW_FOLDERS = "showfolders=true";
	
	
	public DocsPlugin()
	{
		
	}

	public void start(BundleContext context)
		throws Exception
	{
		super.start(context);
		
		plugin = this;
		
		DocsUtils.registerWorkbenchAdapters();
	}

	public void stop(BundleContext context)
		throws Exception
	{
		plugin = null;
		
		super.stop(context);
	}

	/**
	 * Returns the shared instance
	 * 
	 * @return the shared instance
	 */
	public static DocsPlugin getDefault()
	{
		return plugin;
	}

	/**
	 * Returns an image descriptor for the image file at the given plug-in relative path
	 * 
	 * @param path
	 *            the path
	 * @return the image descriptor
	 */
	public static ImageDescriptor getImageDescriptor(String path)
	{
		return imageDescriptorFromPlugin(PLUGIN_ID, path);
	}
	
	/**
	 * Get an image given a path relative to this plugin.
	 * 
	 * @param path
	 * @return
	 */
	public static Image getImage(String path)
	{
		if (getDefault().getImageRegistry().get(path) != null)
			return getDefault().getImageRegistry().get(path);
		
		ImageDescriptor descriptor = findImageDescriptor(path);
		
		if (descriptor != null)
		{
			getDefault().getImageRegistry().put(path, descriptor);
			
			return getDefault().getImageRegistry().get(path);
		}
		
		return null;
	}
	
	/**
	 * Returns an image descriptor for the image file at the given plug-in relative path.
	 * 
	 * @param path
	 *            the path
	 * @return the image descriptor
	 */
	public static ImageDescriptor getBundledImageDescriptor(String path)
	{
		return AbstractUIPlugin.imageDescriptorFromPlugin(PLUGIN_ID, path);
	}

	/**
	 * Respects images residing in any plug-in. If path is relative, then this bundle is looked up
	 * for the image, otherwise, for absolute path, first segment is taken as id of plug-in with
	 * image
	 * 
	 * @param path
	 *            the path to image, either absolute (with plug-in id as first segment), or relative
	 *            for bundled images
	 * @return the image descriptor
	 */
	public static ImageDescriptor findImageDescriptor( String path )
	{
		IPath iPath = new Path(path);
		
		if (iPath.isAbsolute() && iPath.segmentCount() > 1)
		{
			return AbstractUIPlugin.imageDescriptorFromPlugin(iPath.segment(0),
				iPath.removeFirstSegments(1).makeAbsolute().toString());
		}
		else
		{
			return getBundledImageDescriptor(iPath.makeAbsolute().toString());
		}
	}
	
	
	// User credentials section.
	
	private static final URL 	GOOGLE_AUTH_URL;
	
	private static final String AUTH_PASSWORD 	= "org.dcarew.googledocs.password"; 
	private static final String AUTH_USERNAME 	= "org.dcarew.googledocs.username"; 
	
	static
	{
		try
		{
			GOOGLE_AUTH_URL = new URL("http://docs.google.com/");
		}
		catch (MalformedURLException e)
		{
			throw new RuntimeException(e);
		}
	}
	
	public static boolean hasUserCredentials()
	{
		return getUserCredentials() != null;
	}
	
	public static DocsCredentials getUserCredentials()
	{
		Map authMap = Platform.getAuthorizationInfo(GOOGLE_AUTH_URL, "", "");
		
		if (authMap == null)
			return null;
		
		String userName = (String)authMap.get(AUTH_USERNAME);
		String password = (String)authMap.get(AUTH_PASSWORD);
		
		if (userName == null || password == null)
			return null;
		
		return new DocsCredentials(userName, password);
	}
	
	public static void setUserCredentials(String userName, String password)
	{
		if (userName == null)
			throw new IllegalArgumentException();
		
		if (password == null)
			throw new IllegalArgumentException();
		
		Map authMap = Platform.getAuthorizationInfo(GOOGLE_AUTH_URL, "", "");
		
		if (authMap == null)
			authMap = new HashMap();
		
		authMap.put(AUTH_USERNAME, userName);
		authMap.put(AUTH_PASSWORD, password);
		
		try
		{
			Platform.addAuthorizationInfo(GOOGLE_AUTH_URL, "", "", authMap);
		}
		catch (CoreException e)
		{
			log(e);
		}
	}
	
	public static void initializeService()
		throws AuthenticationException
	{
		try
		{
			spreadsheetService = new SpreadsheetService(APPLICATION_NAME);
			docsService = new DocsService(APPLICATION_NAME);
			
			DocsCredentials credentials = DocsPlugin.getUserCredentials();
			
			if (credentials == null)
				throw new AuthenticationException("No username / password provided.");
			
			spreadsheetService.setUserCredentials(credentials.getUserName(), credentials.getPassword());
			docsService.setUserCredentials(credentials.getUserName(), credentials.getPassword());
		}
		catch (AuthenticationException ae)
		{
			spreadsheetService = null;
			
			throw ae;
		}
		catch (RuntimeException re)
		{
			spreadsheetService = null;
			
			throw re;
		}
	}
	
	public static boolean isServiceInitialized()
	{
		return spreadsheetService != null;
	}
	
	public static List getAllGoogleSpreadSheets()
		throws IOException, ServiceException
	{
		if (!isServiceInitialized())
			initializeService();
		
		FeedURLFactory factory = FeedURLFactory.getDefault();
			
		SpreadsheetFeed feed = (SpreadsheetFeed)spreadsheetService.getFeed(factory.getSpreadsheetsFeedUrl(), SpreadsheetFeed.class);
	    
	    return feed.getEntries();
	}
	
	public static List getAllGoogleDocuments()
		throws IOException, MalformedURLException, ServiceException
	{
		if (!isServiceInitialized())
			initializeService();
		
		DocumentListFeed feed = getDocsListFeed("documents");
		
		return feed.getEntries();
	}
	
	public static List getAllGoogleFiles()
		throws IOException, MalformedURLException, ServiceException
	{
		if (!isServiceInitialized())
			initializeService();
		
		DocumentListFeed feed = getDocsListFeed("all");
		
		return feed.getEntries();
	}
	
	public static DocumentListFeed getDocsListFeed(String category)
		throws IOException, MalformedURLException, ServiceException
	{
	  if (category == null)
	    throw new IllegalArgumentException("null category");
	  
	  URL url;
	
	  if (category.equals("all")) {
	    url = buildUrl(URL_GROUP_DOCUMENTS + URL_PATH);
	  } else if (category.equals("folders")) {
	    String[] parameters = {PARAMETER_SHOW_FOLDERS};
	    url = buildUrl(URL_GROUP_DOCUMENTS + URL_PATH + URL_CATEGORY_FOLDER, parameters);
	  } else if (category.equals("documents")) {
	    url = buildUrl(URL_GROUP_DOCUMENTS + URL_PATH + URL_CATEGORY_DOCUMENT);
	  } else if (category.equals("spreadsheets")) {
	    url = buildUrl(URL_GROUP_DOCUMENTS + URL_PATH + URL_CATEGORY_SPREADSHEET);
	  } else if (category.equals("pdfs")) {
	    url = buildUrl(URL_GROUP_DOCUMENTS + URL_PATH + URL_CATEGORY_PDF);
	  } else if (category.equals("presentations")) {
	    url = buildUrl(URL_GROUP_DOCUMENTS + URL_PATH + URL_CATEGORY_PRESENTATION);
	  } else if (category.equals("starred")) {
	    url = buildUrl(URL_GROUP_DOCUMENTS + URL_PATH + URL_CATEGORY_STARRED);
	  } else if (category.equals("trashed")) {
	    url = buildUrl(URL_GROUP_DOCUMENTS + URL_PATH + URL_CATEGORY_TRASHED);
	  } else {
	    return null;
	  }
	  
	  return (DocumentListFeed)docsService.getFeed(url, DocumentListFeed.class);
	}
		
	public static SpreadsheetEntry openSpreadSheet(String spreadsheetID)
		throws IOException, ServiceException
	{
		if (!isServiceInitialized())
			initializeService();
		
		SpreadsheetEntry spreadsheet = new SpreadsheetEntry();
		
		spreadsheet.setService(spreadsheetService);
		spreadsheet.setId(spreadsheetID);
		
		return (SpreadsheetEntry)spreadsheet.getSelf();
	}

	
	// /////////////////////////////////////////////////////////////////////////////
	//
	// Logging
	//
	// /////////////////////////////////////////////////////////////////////////////

	/**
	 * Logs a warning with this plug-in's log.
	 * 
	 * @param message the warning message
	 */
	public static void logWarning(String message)
	{
		Status errorStatus = new Status(IStatus.WARNING, PLUGIN_ID, IStatus.OK, message, null);
		log(errorStatus);
	}
	
	/**
	 * Logs an error with this plug-in's log.
	 * 
	 * @param message the error message
	 */
	public static void logError(String message)
	{
		log(new Status(IStatus.ERROR, PLUGIN_ID, IStatus.OK, message, null));
	}
	
	/**
	 * Logs the specified status with this plug-in's log.
	 * 
	 * @param status status to log
	 */
	public static void log(IStatus status)
	{
		getDefault().getLog().log(status);
	}
	
	/**
	 * Logs the specified throwable with this plug-in's log.
	 * 
	 * @param t throwable to log
	 */
	public static void log(Throwable t)
	{
		log(newErrorStatus("Java NCSS Plugin", t));
	}
	
	/**
	 * Returns a new error status for this plug-in with the given message
	 * 
	 * @param message the message to be included in the status
	 * @param exception the exception to be included in the status or <code>null</code> if none
	 * @return a new error status
	 */
	public static IStatus newErrorStatus(String message, Throwable exception)
	{
		return new Status(IStatus.ERROR, PLUGIN_ID, message, exception);
	}
	
	/**
	   *
	   * @param path the path to add to the protocol/host
	   *
	   * @throws MalformedURLException
	   * @throws DocumentListException
	   */
	  private static URL buildUrl(String path) throws MalformedURLException {
	    return buildUrl(path, null);
	  }

	  /**
	   * Builds a URL with parameters.
	   *
	   * @param path the path to add to the protocol/host
	   * @param parameters parameters to be added to the URL.
	   *
	   * @throws MalformedURLException
	   * @throws DocumentListException
	   */
	  private static URL buildUrl(String path, String[] parameters)
	      throws MalformedURLException {
		  
	    return buildUrl(DEFAULT_HOST, path, parameters);
	  }

	  /**
	   * Builds a URL with parameters.
	   *
	   * @param host the domain of the server
	   * @param path the path to add to the protocol/host
	   * @param parameters parameters to be added to the URL.
	   *
	   * @throws MalformedURLException
	   * @throws DocumentListException
	   */
	  private static URL buildUrl(String host, String path,  String[] parameters)
	      throws MalformedURLException {

	    StringBuffer url = new StringBuffer();
	    url.append(DEFAULT_PROTOCOL + "://" + host + URL_FEED + path);
	    
	    if (parameters != null && parameters.length > 0) {
	      url.append("?");
	      for (int i = 0; i < parameters.length; i++) {
	        url.append(parameters[i]);
	        if (i != (parameters.length - 1)) {
	          url.append("&");
	        }
	      }
	    }

	    return new URL(url.toString());
	  }

	  
}
